package 基础;

import java.nio.charset.Charset;
import java.lang.String;
public class 编码 {
	public static void main(String[] args) {
			String hexString = "0123456789ABCDEF";
		    final char[] hexCode = "0123456789ABCDEF".toCharArray();
		    //string2Hex  普通字符串 转 16进制字符串
		    String str="中文Aabc";
			StringBuilder builder = new StringBuilder();
			byte[] bytes=str.getBytes(Charset.forName("gb2312"));//D6D0CEC441616263
//			byte[] bytes = str.getBytes(Charset.forName("unicode"));//FEFF4E2D65870041006100620063
//			byte[] bytes = str.getBytes(Charset.forName("utf-8"));//E4B8ADE6968741616263
//			byte[] bytes = str.getBytes(Charset.forName("iso8859-1"));//3F3F41616263 中文无编码
			int bit;
			for (byte b : bytes) {
				bit = (b & 0x0F0) >> 4;
				builder.append(hexCode[bit]);
				bit = b & 0x0F;
				builder.append(hexCode[bit]);
			}
			System.out.println(builder.toString().trim());
			
			//hex2String  16进制字符串 转 普通字符串  注：字符集编码必须一致。
			char[] hexs=builder.toString().toCharArray();
			byte[] bytes1=new byte[hexs.length/2];
			int n;
			for (int i=0;i<hexs.length/2;i++) {
				n=hexString.indexOf(hexs[2*i])*16;//高位字节
				n+=hexString.indexOf(hexs[2*i+1]);//低位字节
				bytes1[i]=(byte) n;
			}
			
			System.out.println(new String(bytes1,Charset.forName("gb2312")));
		}
}

/*
字符串和编码

  计算机只能处理数字，如果处理文本，就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特（bit)作为一个字节(byte),所以，一个字节能表示的最大的整
数就是255(二进制11111111=十进制255),如果要表示更大的整数，就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节表示的最大整数是4294967295。
   由于计算机是美国人发明的，因此，最早只有127个字母被编码到计算机里，也就是大小写英文字母、数字和一些符号，这个编码表被称为ASCII编码，比如大小写字母A的编码
是65,小写字母的编码是122.
   但是要处理中文显然一个字节是不够的，至少需要两个字节，而且还不能和ASCII编码冲突，所以，中国制定了GB2312编码，用来把中文编进去。日本人把日文编到shift_JIS里
，韩国把韩文编到Euc-kr，各国有各国的标准，就会不可避免地发生冲突，结果就是，在多语言混合的文本中，显示出来就会有乱码。
   因此，Unicode应运而生，Unicode把所以语言都统一到一套编码里，这样就不会再有乱码问题了。Unicode标准也在不断发展，但最常见的是用两个字节表示一个字符(如果要
用到非常偏僻的字符，就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。
   ASCII编码和Unicode编码的区别：ASCII编码是1个字节，而Unicode编码通常是2个字节。字母A用ASCII编码是十进制的48，二进制的01000001；字符0用ASCII编码
是十进制的48，二进制的00110000，注意字符‘0’和整数0是不同的；汉字“中”已经超出ASCII编码的范围，用Unicode编码是十进制的20013，二进制的01001110 00101101.
   ASCII编码的A用Unicode编码，只需要在前面补0就可以，因此，A的Unicode编码是00000000 01000001.
   新的问题又出现了：如果统一成Unicode编码，乱码问题从此消失了，但是，如果你写的文本基本上全是英文的话，用Unicode编码比ASCII编码需要多一倍的存储空间，在存储
和传输是就十分不划算。所以本着节约的精神，又出现了把Unicode编码转化为“可变长的编码“的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节，
常用的英文字母被编成1个字节，汉字通常是3个字节，只有很生僻的字符才会被编成4-6个字节。如果你要传输文本包含大量英文字符，用UTF-8编码就能节省空间：

  字符     ASCII         Unicode                   UTF-8
   A     01000001   00000000 01000001     01000001
   中     X          01001110 00101101     11100001 10111000 10101101
   从上面的表格还可以发现，UTF-8编码有一个额外的好处，就是ASCII编码实际上可以被看成是UTF-8编码的一部分，所以，大量只支持ASCII编码的历史遗留软件可以在UTF-8编
码下继续工作。
   在计算机内存中，统一使用Unicode编码，当需要保存到硬盘或者需要传输的时候，就转换为UTF-8编码。
   用记事本编辑的时候，从文件读取的UTF-8字符被转换为Unicode字符到内存里，编辑完成后，保存的时候再把Unicode转换为UTF-8保存到文件。
   浏览网页的时候，服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器。

 一.编码基础知识 最早的编码是iso 8859-1,和ascII相似。但为了方便表示各种各样的语言，逐渐出现了很多标准编码：

1.ISO8859-1:通常叫做Latin-1,属于单字节编码，最多能表示的字符范围是0-255,应用于英文系列。比如,字母a的编码为0x61=97.很明显，iso8859-1编码表示的字符范
围很窄，无法表示中文字符。但是，由于是单字节编码，和计算机最基础的表示单位一致，所有很多时候，仍旧使用iso8859-1编码来表示。而且在很多协议上，默认使用该编码。
比如，虽然 '中文'两字不存在iso8859-1编码，以gb2312编码为例，应该是'd6d0cec4'两个字符(java字符占2个字节),使用iso8859-1编码的时候则将他拆开为4个字节
来表示:'d6 d0 ce c4' (事实上,在存储的时候,也是以字节为单位处理的)。而如果是utf编码，则是6个字节'e4 b8 ad e6 96 87'.很明显,这种表示方法还需要以另一种
编码为基础。

2.GB2312/GBK:这就是汉字的国标码,专门用来表示汉字,是双字节编码，而英文字母和iso8859-1一致(兼容iso8859-1编码)。其中gbk编码能够用来表示繁体字和简体字，
而gb2312只能表示简体字,gbk是兼容gb2312编码的。

3.unicode:这是最统一的编码，可以用来表示所有语言的字符，而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的，也不兼容
任何编码。不过相对于iso8859-1编码来说，unicode编码只是在前面增加了一个0字节， 如：字母a为'00 61'.

需要说明的是:定长编码便于计算机处理(gb2312/gbk不是定长编码)，而unicode又可以用来表示所有字符，所以，很多软件内部是使用unicode编码来处理的比如java.

4.UTF:考虑到unicode不兼容iso8859-1编码，而且容易占用更多的空间:因为对于英文字母unicode也需要用两个字节类表示。所以,unicode不便于传输和存储。因而产生
了utf编码,utf编码兼容iso8859-1编码，同时也可以用来表示所有语言的字符，不过,utf编码是不定长编码，每一个字符的长度从1-6给字节不等。另外utf编码自带简单的校验
功能。一般来讲，英文字母都是用一个字节表示，而汉字使用三个字节。注意:虽然说utf是为了使用更少的空间而使用的，但那只是相对于unicode编码来说，如果已经知道是汉字，
则使用gb2312/gbk无疑是最节省的。不过另一方面，值得说明的是，虽然utf编码对汉字使用3个字节，但即使对于汉字网页，utf编码也会比unicode编码节省，因为网页中包含
了很多的英文字符。

unicode,utf-8,iso8859-1之间区别： 
'中文': gb2312编码： 'd6d0 cec4' 
       unicode编码:'4e2d 6587'
       utf-8编码：  'e4b8ad e69687'
注意：这两个汉字没有iso 8859-1编码，但可以用iso 8859-1编码来表示

utf-8编码可以用gbk和iso 8859-1解码编回去 gbk编码只能用iso 8859-1解码编回去

中文字符集编码iso8859-1,Unicode,gb2312,cp936,GBK,GB18030

转自： http://www.blog.edu.cn/user3/flyingcs/archives/2006/1418577.shtml 概要：UTF-8的一个特别的好处是它与ISO
8859-1完全兼容，可以表示世界上所有的字符，汉字通常用３个字节来表示。GB2312的code page是CP20936。GBK的code page是CP936
。GB18030支持的字符数更多。GB2312、GBK、GB18030均为双字节。

 问题一： 使用Windows记事本的“另存为”，可以在GBK、Unicode、Unicode bigendian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows是怎样识别编
 码方式的呢？

我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE(Unicode),FE、FF(Unicode big
endian),EF、BB、BF(UTF-8)。 但这些标记是基于什么标准呢？

 问题二：
 最近在网上看到一个ConvertUTF.c，实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式，我原来就了解。
但这个程序让我有些糊涂，想不起来UTF-16和UCS2有什么关系。

查了查相关资料，总算将这些问题弄清楚了，顺带也了解了一些Unicode的细节。作者写成一篇文章，送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂，但要求读者知道什
么是字节，什么是十六进制。

0、big endian和little endian big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码
是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面？如果将6C写在前面，就是big endian(高位在前)。如果将49写在前面，就是little endian(低位在前)。

print(hex(ord('汉')))#0x6c49

“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，一个皇帝送了命
，另一个丢了王位。 我们一般将endian翻译成“字节序”，将big endian和little endian称作“大尾”和“小尾”。

1、字符编码、内码，顺带介绍汉字编码字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设
计了用于简体中文的GB2312和用于繁体中文的big5。
   GB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个
空位是D7FA-D7FE。
    0xB0:176
    0xF7:247
    247-176=71
    
    0xFE:
    0xA1:
         =93
	print(int(0xF7)-int(0xB0))#71
	print(int(0xFE)-int(0xA1))#93   

【区位码】

1.整个GB2312字符集分成94个区，每区有94个位，每个区位上只有一个字符，即每区含有94个汉字或符号，用所在的区和位来对字符进行编码(实际上就是码点值、码点编号、字符编号)，
因此称为区位码(或许叫“区位号”更为恰当)。

换言之，GB2312将包括汉字在内的所有字符编入一个94*94的二维表，行就是“区”、列就是“位”，每个字符由区、位唯一定位，其对应的区、位编号合并就是区位码。

比如“万”字在45区82位，所以“万”字的区位码是：45 82（注意，GB类汉字编码为双字节编码，因此，45相当于高位字节，82相当于低位字节）。

2.GB2312字符集中：

1）01~09区(682个)：特殊符号、数字、英文字符、制表符等，包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母等在内的682个全角字符；

2）10~15区：空区，留待扩展；

3）16~55区(3755个)：常用汉字(也称一级汉字)，按拼音排序；

4）56~87区(3008个)：非常用汉字(也称二级汉字)，按部首/笔画排序；

5）88~94区：空区，留待扩展。

在使用GB2312的程序中，通常采用EUC储存方法，以便兼容于ASCII。浏览器编码表上的“GB2312”，通常都是指“EUC-CN”表示法。

每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”（也称“区字节）”，第二个字节称为“低位字节”（也称“位字节”）。

“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0)，“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始，汉字区的“高位字节”的
范围是0xB0-0xF7，“低位字节”的范围是0xA1-0xFE，占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。

例如“啊”字在大多数程序中，会以两个字节，0xB0（第一个字节） 0xA1（第二个字节）储存。区位码=区字节+位字节（与区位码对比：0xB0=0xA0+16,0xA1=0xA0+1）。

两种不同的GB2312实现
有两种不同的GB2312实现，在它们之间存在少量的差别，其中至少有一个是错误的。

字节序	实现A	实现B
A1A4	U+00B7 MIDDLE DOT	U+30FB KATAKANA MIDDLE DOT
A1AA	U+2014 EM DASH	U+2015 HORIZONTAL BAR
实现A与GBK/GB18030兼容，实现B则不兼容。

在2015年， 微软.Net Framework在使用实现A。 iconv-1.14， php-5.6， ActivePerl-5.20， Java 1.7， Python 3.4在使用实现B。

编码表
GB2312简体中文编码表

code +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F

A1A0 　 、 。 · ˉ ˇ ¨ 〃 々 — ～ ‖ … ‘ ’

A1B0 “ ” 〔 〕 〈 〉 《 》 「 」 『 』 〖 〗 【 】

A1C0 ± × ÷ ∶ ∧ ∨ ∑ ∏ ∪ ∩ ∈ ∷ √ ⊥ ∥ ∠

A1D0 ⌒ ⊙ ∫ ∮ ≡ ≌ ≈ ∽ ∝ ≠ ≮ ≯ ≤ ≥ ∞ ∵

A1E0 ∴ ♂ ♀ ° ′ ″ ℃ $ ¤ ￠ ￡ ‰ § № ☆ ★

A1F0 ○ ● ◎ ◇ ◆ □ ■ △ ▲ ※ → ← ↑ ↓ 〓

code +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F

A2A0 Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ ? ? ? ? ?

A2B0 ? ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖

A2C0 ⒗ ⒘ ⒙ ⒚ ⒛ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾

A2D0 ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ① ② ③ ④ ⑤ ⑥ ⑦

A2E0 ⑧ ⑨ ⑩ ? ? 一 二 三 四 五 六 七 八 九 十 ?

A2F0 ? Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ Ⅶ Ⅷ Ⅸ Ⅹ Ⅺ Ⅻ ? ?

code +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F

A3A0 ！ " # ￥ % & ' （ ） * + ， － ． /

A3C0 @ A B C D E F G H I J K L M N O

A3D0 P Q R S T U V W X Y Z [ \ ] ^ _

A3E0 ` a b c d e f g h i j k l m n o

A3F0 p q r s t u v w x y z { | }

……


GB2312 把每个汉字都编码成两个字节，第一个字节是高位字节，第二个字节是低位字节

GB2312 为了兼容 ASICII ，其编码需要进行一些转换才能避免和 ASICII 编码重叠，转换的过程涉及到区位码和国标码的概念，下面说明转成内码的过程

【区位码】
GB2312 对汉字进行了分区处理，每个区含有 94 个汉字或者字符，总共有 94 个区，每个汉字或者字符都对应一个 分区编号和分区内的位置编号，称为 区位码

比如：汉字 "中" 字的 分区编号是 54，分区内位置编号是 48，所以，"中" 字的区位码是 54 48

【国标码】
国标码 也叫 交换码，用于交换文件所使用的编码，在早期，不同的操作系统可能使用不同的内码，如果它们之间要交换文件，则会发生乱码的现象，当时的解决方法是交换文件之前先转成交换码再交换，接收者收到之后再转成内码

交换码是比较早期的一种方案，目前系统大都采用内码作为交换码

ASICII 码为 0- 31 的这 32 个字符是不可显示的字符，为了避免和这些字符的码点冲突，将 分区编号和分区内位置编号都加上 32 ，把这个转换的结果称为 国标码

比如：汉字 "中" 字分区编号是 54，分区内位置编号是 48，加上 32 之后，分区编号是 54 + 32 = 86, ，分区内位置编号是 48 + 32 = 80，所以 "中" 字 的国标码是 86 80

【内码】
国标码 和 ASICII 码还是存在一定的重复，比如 "中" 字 的国标码是 86 80，对应第一个字节是 86，第二个字节是 80，而在 ASICII 码中它们分别代表大写字母V 和 大写字母 P，这就无法区分它们到底是一个汉字，还是两个字母

为了解决这一点，把国标码中的每个字节的最高位置为 1，也即相当于每个字节都加上 128 ( 2的7次方 )，还是以 "中" 字为例，它的 国标码是 86 80，加上 128 后， 第一个字节是 86 + 128 = 214， 第二个字节是 80 + 128 = 208，转化成 16 进制是 0xD6 0xD0 ( 214 的十六进制是 0xD6， 208 的十六进制是 0xD0 )

国标码的每个字节都加上 128 后，得到国标码的机内码，简称 内码，汉字是以内码的形式在计算机中存储和传播的

上面介绍 区位码 和 国标码，主要是是为了说明 汉字内码是如何一步一步发展而来的

可以看出，汉字的 区位码 + 32 + 128 就得到了内码，进一步简化，区位码 + 32 + 128 = 区位码 + 160 = 区位码 + 0xA0（128 的十六进制） , 因此 内码 = 区位码 + 0xA0

比如："中" 字的区位码是 54 48，对应的十六进制是0x36 0x30，因此它的内码为 (0x36 + 0xA0) (0x30 + 0xA0)，也即 0xD6 0xD0

关于汉字的区位码请参考：汉字区位码

GB2312 有效的编码范围如下图所示

字节数        GB2312的编码空间             编码数
---------------------------------------------
单字节          0x00-0x7F                128
双字节      第一字节      第二字节   
        0xA1--0xA9   0xA9--0xFE        846
        0xB0--0xF7   0xA1--0xFE        6768



GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的GB18030是取代GBK1.0的正式国
家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030，对嵌入式产品暂不作要求。所以手机、MP3一般只
支持GB2312。
从ASCII、GB2312、GBK到GB18030，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一
地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。
有的中文Windows的缺省内码还是GBK，可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符，普通人是很难用到的，通常我们还是用GBK指代中文Windows
内码。

这里还有一些细节： GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0(1010)。 在双字节字符集(DBCS)中，GB内码的存储格式始终
是big endian，即高位在前。GB2312(包括6763个汉字和682个其它符号)的两个字节的最高位都是1(一个字节共8位，最高位为1,其它7位编码范围为128)。但符合这个条件的
码位只有128*128=16384个。所以GBK(21886个符号)和GB18030(收录了27484个汉字)的低字节最高位都可能不是1。(确保高字节最高位为1,低字节最高位可以不为1,以增
加编码范围)不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。

2、Unicode、UCS和UTF
前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说，是与ISO-8859-1兼容),与GB码不兼容.

例如“汉”字的编码:
Unicode码:6C49      ==>print(hex(ord('汉')))#0x6c49
GB码:BABA           ==>print('汉'.encode('gb2312'))#b'\xba\xba'
UTF-8码:E6 B1 89    ==>print('汉'.encode('utf-8'))#b'\xe6\xb1\x89'

Unicode也是一种字符编码方法，不过它是由国际组织设计，可以容纳全世界所有语言文字的编码方案。Unicode的学名
是 "Universal Multiple-Octet Coded Character Set"，简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。 根据维基百科全
书( http://zh.wikipedia.org/wiki/)的记载：历史上存在两个试图独立设计Unicode的组织，即国际标准化组织（ISO）和一个软件制造商的协会（unicode.org)。
ISO开发了ISO 10646项目，Unicode协会开发了Unicode项目。
在1991年前后，双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果，并为创立一个单一编码表而协同工作。从Unicode2.0开始，Unicode项目采
用了与ISO 10646-1相同的字库和字码。 目前两个项目仍都存在，并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准
是ISO 10646-3:2003。
UCS只是规定如何编码，并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是 6C49，我可以用4个ascii数字来传输、保存这个编码；也可以用utf-8编码:3个连续的
字节 E6 B1 89(print('汉'.encode('utf-8')))来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处
是它与ISO-8859-1完全兼容。
UTF是“UCS Transformation Format”的缩写。IETF的RFC2781和RFC3629以RFC的一贯风格，清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记
不得IETF是Internet Engineering Task Force(因特网工程任务组)的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。

2.1、内码和code page
目前Windows的内核已经采用Unicode编码,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码，例如GBK，Windows不
可能不支持现有的编码，而全部改用Unicode。
Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。 微软也为GB18030定义
了code page：CP54936。但是由于GB18030有一部分4字节编码，而Windows的代码页只支持单字节和双字节编码，所以这个code page是无法真正使用的。

3、UCS-2、UCS-4、BMP
UCS有两种格式：UCS-2和UCS-4。顾名思义，UCS-2就是用两个字节编码，UCS-4就是用4个字节(实际上只用了31位，最高位必须为0)编码。下面让我们做一些简单的数学游戏：
UCS-2有2^16=65536个码位，UCS-4有2^31=2147483648个码位。
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行(rows)，每行包含256个cells。当然同一行的cells只是最后一个字节不同，其余都相同。
group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中，高两个字节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节，就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
group0  (0000 0000)-->plane0  (0000 0000)-->rows0  (0000 0000)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
                                         ...
                                         -->rows255(1111 1111)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
                   ...
                   -->plane255(1111 1111)-->rows0  (0000 0000)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
                                         ...
                                         -->rows255(1111 1111)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
...
group255(1111 1111)-->plane0  (0000 0000)-->rows0  (0000 0000)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
                                         ...
                                         -->rows255(1111 1111)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
                   ...
                   -->plane255(1111 1111)-->rows0  (0000 0000)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)
                                         ...
                                         -->rows255(1111 1111)-->cells0(0000 0000)
                                                              ...
                                                              -->cells255(1111 1111)

4、UTF编码 UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：
UCS-2编码(16进制)   UTF-8 字节流(二进制):
     0000 - 007F   0xxxxxxx                            128
     0080 - 07FF   110xxxxx 10xxxxxx                   2048
     0800 - FFFF   1110xxxx 10xxxxxx 10xxxxxx          16384 18560
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间，所以肯定要用3字节模板了： 1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：
00000110 00110001 00001001，用这个比特流依次代替模板中的x，得到：11100110 10110001 10001001，即E6 B1 89。
读者可以用记事本测试一下我们的编码是否正确。需要注意，UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16，可能产生混淆。你可以在设置中关掉这个选项。更好的
工具是Hex Workshop。

--------------------------------------------------------------------------------------------------------------------
UTF-8用1~4个字节对Unicode进行编码。从Unicode到UTF-8的编码方式如下：

000000 - 00007F║0xxxxxxx                               128
000080 - 0007FF║110xxxxx 10xxxxxx                      2048
000800 - 00FFFF║1110xxxx 10xxxxxx 10xxxxxx             16384
010000 - 10FFFF║11110xxx 10xxxxxx 10xxxxxx 10xxxxxx    18560
对于0x00-0x7F之间的字符，UTF-8编码与ASCII编码完全相同；
带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码（Unicode范围由）；
其他基本多文种平面（BMP）中的字符（这包含了大部分常用字）使用三个字节编码； 其他极少使用的Unicode 辅助平面的字符使用四字节编码；
UTF-8编码的最大长度是4个字节。从上表可以看出，4字节模板有21个x，即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。

UTF-8解析算法： 如果字节(Byte)的第一位为0，则B为ASCII码，并且Byte独立的表示一个字符;
如果字节(Byte)的第一位为1，第二位为0，则Byte为一个非ASCII字符（该字符由多个字节表示）中的一个字节，并且不为字符的第一个字节编码;
如果字节(Byte)的前两位为1，第三位为0，则Byte为一个非ASCII字符（该字符由多个字节表示）中的第一个字节，并且该字符由两个字节表示;
如果字节(Byte)的前三位为1，第四位为0，则Byte为一个非ASCII字符（该字符由多个字节表示）中的第一个字节，并且该字符由三个字节表示;
如果字节(Byte)的前四位为1，第五位为0，则Byte为一个非ASCII字符（该字符由多个字节表示）中的第一个字节，并且该字符由四个字节表示。
--------------------------------------------------------------------------------------------------------------------

UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码，UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码，定义了一个算法。不过由于实际使用的UCS2，或者UCS4的BMP必然小于0x10000，所以就目前而言，可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案，UTF-16却要用于实际的传输，所以就不得不考虑字节序的问题。

5、UTF的字节序和BOM
UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E，
“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”，那么这是“奎” 还 是“乙”？ Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of
Material”的BOM表 ，而是Byte Order Mark。BOM是一个有点小聪明的想法： 在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK
SPACE"的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符"ZERO WIDTH NO-BREAK
SPACE"。 这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK
SPACE"又被称作BOM。 UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB
BF(读者可以用我们前面介绍的编码方法验证一下).所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。 Windows就是使用BOM来标记文本文件的编码方式的。

6、进一步的参考资料 本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" ( http://ww
w.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html )。 我还找了两篇看上去不错的资料，不过因为我开始的疑问都找到了答案，所以就没有看：
"Understanding Unicode A general introduction to the Unicode Standard" ( http:
//scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a ) " Character set
encoding basics Understanding character set encodings and legacy encodings" (
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id =IWS-Chapter03 )
我写过UTF-8、UCS-2、GBK相互转换的软件包，包括使用Windows API和不使用Windows API的版本。以后有时间的话，我会整理一下放到我的个人主页上( http:
//fmddlmyy.home4u.china.com )。 附录1 再说说区位码、GB2312、内码和代码页 有的朋友对文章中这句话还有疑问：
“GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。” 我再详细解释一下：
“GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准信息交换用汉字编码字符集基本集GB
2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”，第二个数称为“位”。所以也称为区位码。1-9区是中文符号，16-55
区是一级汉字，56-87区是二级汉字。现在Windows也还有区位输入法，例如输入1601得到“啊” 。
内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在内部统一使用Unicode，然后用代码页适应各种语言,“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码，在特殊的场合也会说自己的内码是Unicode，
例如在 GB18030问题的处理上。 所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936 ，BIG5的code
page是CP950，GB2312的code page是CP20936。
Windows中有缺省代码页的概念，即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件，里面的内容是字节流：BA、BA、D7、D6。Windows应该去怎么解释它呢 ？
是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释，还是按照ISO8859-1去解释？如果按GBK去解释，就会得到“汉字”两个字。按照其它编码解释，可能找不到对应的字符，也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符，这时就产生了乱码。
答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI，其实就是按照缺省代码页的编码方法保存。
Windows的内码是Unicode，它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码，用户又安装了对应的代码页，Windows就能正确显示，例如在HTML文件中就可以指定charset。
有的HTML文件作者，特别是英文作者，认为世界上所有人都使用英文，在文件中不指定charset。如果他使用了0x80-0xff之间的字符，中文Windows又按照缺省的GBK去解释，就会出现乱码。这时只要在这个html文件中加上指定charset的语句，例如：如果原作者使用的
代码页和ISO8859-1兼容，就不会出现乱码了。 再说区位码，啊的区位码是1601，写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的
ASCII编码，我们在区位码的高、低字节上分别加上A0。这样 “啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码，虽然GB2312的原文根本没提到这一点。

*/